//*****************************************************************************
//* Foxit Digital Rights Management Library
//*
//* Foxit Software Inc.
//* Copyright(C) 2005-2010, all rights reserved.
//*
//* The following code is copyrighted and contains proprietary information
//* and trade secrets of Foxit Software Inc.
//*****************************************************************************
/**
 * @addtogroup FDRM
 * @{
 */

/**
 * @file
 * @brief PDF Security Handler
 */

#ifndef _FDRM_PDF
#define _FDRM_PDF

#ifndef _FPDF_PARSER_
	#include "../fpdfapi/fpdf_parser.h"
#endif

class FDRM_PDFCRYPTOCONTEXT;

#define FDRM_PDFSECURITYHANDLER_NAME	"FoxitDRM"

/** @brief Customized security handler with 256 bytes key for encryption each object content. 
  * And set a dictionary as underline in a encrypted pdf file.
  *
  77 0 obj
  << /Filter /FoxitDRM
  /SubFilter /OnDemandCM
  /Vender ()
  /Identity <<
	/IssuerID ()            
	/FileID ()              
	/Version ()             
	/Logo <>               
  >>
  /Validity <<              hash value for ensure preceding ditionary entries
  /Algorithm ()
  /Data <>
  >> 
  /V 2
  /R 2
  */
class CFDRM_PDFSecurityHandler : public CPDF_SecurityHandler
{
public:
	CFDRM_PDFSecurityHandler();
	virtual ~CFDRM_PDFSecurityHandler();

	virtual FX_BOOL		Init(CPDF_Parser *pParser, CPDF_Dictionary *pEncryptDict);

	virtual FX_DWORD	GetPermissions();
	virtual FX_BOOL		IsOwner();

	
	/**
	 * @brief	provide cipher algorithm mark, key , key length
	 * CFDRM_PDFCryptoHandler object will call this, if it created from 
	 * CreateCryptoHandler
	 * @param[in] bsSubFilter		cipher algorithm mark.
	 * @param[in] buffer			key buffer.
	 * @param[in] 
	 */
	virtual FX_BOOL		GetCryptInfo(int &cipher, FX_LPCBYTE &buffer, int &keylen);

	/**
	 * @brief	set subfilter item in encryption dictionary, for identifing project or version.
	 * @param[in] bsSubFilter		subfilter name.
	 */
	virtual FX_BOOL 	SetSubFilter(FX_BSTR bsSubFilter);
	
	/**
	 * @brief	set entry in identity dictionary
	 * @param[in] bsEntryName		entry name.
	 * @param[in] bsEntryValue		entry value.
	 */
	virtual FX_BOOL 	SetIdentityEntry(FX_BSTR bsEntryName, FX_BSTR bsEntryValue);

	virtual FX_BOOL 	GetSubFilter(CFX_ByteString &bsSubFilter);
	virtual FX_BOOL 	GetIdentityEntry(FX_BSTR bsEntryName, CFX_ByteString &bsEntryValue);

	/**
	 * @brief	Get the current vender identity code.
	 *
	 * @param[out] bsVender		Vender code.
	 *
	 * @return	TRUE if retrieves successfully, or FALSE if fails.
	 */
	virtual FX_BOOL		GetVender(CFX_ByteString &bsVender);

	/**
	 * @brief	set entry in identity dictionary
	 * @param[in] nCipher			cipher algorithm mark , such as FXCIPHER_AES
	 * @param[in] pKeyBuf			key data.
	 * @param[in] nKeylen			key length
	 */
	virtual FX_BOOL		SetCryptInfo(int nCipher, const FX_LPCBYTE pKeyBuf, int nKeylen);

	/**
	 * @brief	Verify the encryption dictionary hash value.
	 */
	virtual FX_BOOL		CheckValidity();

	/**
	 * @brief	Create a cyrptohandler, must be call after SetCryptoInfo
	 */
	virtual CPDF_CryptoHandler*	CreateCryptoHandler();

	/** 
	 * @brief   Whether the metadata is encrypted.
	 */
	virtual FX_BOOL		IsMetadataEncrypted() { return m_bMetaDataEncrypted; }

	/** 
	 * @brief	Set the metadata encrypted state.
	 */
			void		SetMetaDataEncrypted(FX_BOOL bMetaDataEncrypted) { m_bMetaDataEncrypted = bMetaDataEncrypted; } 

	CPDF_Parser*		m_pParser;

protected:
	/**
	 * @brief	Overload the function in base class with same name.
	 */
	virtual FX_BOOL		OnInit(CPDF_Parser *pParser, CPDF_Dictionary *pEncryptDict);
	
	/**
	 * @brief	Parse encryption dictionary and extract information from it.
	 */
	void				ParseEncryptDic();

	CPDF_Dictionary *		m_pDictionary;
	FX_INT32				m_nCipher;
	CFDRM_ByteKeyString		m_Key;
	CFX_ByteString			m_bsFilter;
	CFX_ByteString			m_bsSubFilter;
	CFX_ByteString			m_bsDigest;
	CFX_ByteString			m_bsAlgorithm;
	CFX_CMapByteStringToPtr m_IdentityEntry;
	CPDF_CryptoHandler *	m_pCryptoHandler;
	FX_BOOL					m_bMetaDataEncrypted;
};

/** 
  *	@brief Do concrete encryption / decryption job.
  */
class CFDRM_PDFCryptoHandler : public CPDF_CryptoHandler
{
public:
	CFDRM_PDFCryptoHandler();
	virtual ~CFDRM_PDFCryptoHandler();

/** 
  *	@brief Init PDF cryptoHandler 
  */
	virtual FX_BOOL		Init(CPDF_Dictionary *pEncryptDict, CPDF_SecurityHandler *pSecurityHandler);

/** 
  *	@brief estimate decrypted size, in this case, always returns 0, no estimation. 
  */
	virtual FX_DWORD	DecryptGetSize(FX_DWORD src_size);
/** 
  *	@brief start decryption.
  */
	virtual FX_LPVOID	DecryptStart(FX_DWORD objnum, FX_DWORD gennum);
/** 
  *	@brief decrypt stream.
  */
	virtual FX_BOOL		DecryptStream(FX_LPVOID context, FX_LPCBYTE src_buf, FX_DWORD src_size, CFX_BinaryBuf &dest_buf);
/** 
  *	@brief decryption has done.
  */
	virtual FX_BOOL		DecryptFinish(FX_LPVOID context, CFX_BinaryBuf &dest_buf);

/** 
  *	@brief estimated encrypted size.
  */
	virtual FX_DWORD	EncryptGetSize(FX_DWORD objnum, FX_DWORD version, FX_LPCBYTE src_buf, FX_DWORD src_size);
/** 
  *	@brief Do encryption
  */
	virtual FX_BOOL		EncryptContent(FX_DWORD objnum, FX_DWORD version, FX_LPCBYTE src_buf, FX_DWORD src_size,
									   FX_LPBYTE dest_buf, FX_DWORD &dest_size);

protected:
	FX_BOOL				Calc(FX_DWORD objnum, FX_DWORD gennum, FX_BOOL bEncrypt, FDRM_PDFCRYPTOCONTEXT* pCC);

	CPDF_Dictionary			* m_pDictionary;
	CPDF_SecurityHandler	* m_pSecurityHandler;
};

//*****************************************************************************
//* PDF Schema
//*****************************************************************************
FX_DEFINEHANDLE(FDRM_HPDFSCHEMA);

enum FDRM_PDFSCHEMAITEM
{
	FDRM_PDFSCHEMA_Trailer = 0,		//Trailer dictionary and its sub dictionaries.
	FDRM_PDFSCHEMA_Catalog,			//Catalog dictionary and page-tree dictionaries.
	FDRM_PDFSCHEMA_Page,			//Page dictionary and its sub dictionaries.
};

class IFDRM_PDFSchema : public CFDRM_Object
{
public:
	virtual FDRM_HPDFSCHEMA		StartGeneration(CPDF_Parser *pPDFParser, IFX_FileWrite *pSchemaFile) = 0;
	virtual FX_INT32			ContinueGeneration(FDRM_HPDFSCHEMA hPDFSchema, IFX_Pause *pPause = NULL) = 0;
	virtual void				EndGeneration(FDRM_HPDFSCHEMA hPDFSchema) = 0;

	virtual FX_INT32			LoadSchema(IFX_FileRead *pSchemaFile) = 0;
	virtual FX_INT32			GetPDFFileSize() = 0;
	virtual FX_INT32			GetXRefOffset() = 0;
	virtual FX_INT32			CountPages() = 0;
	virtual FX_INT32			GetSchemaInfo(FX_DWORD dwItem, FX_DWORD dwIndex, CFX_DWordArray &pdfObjs) = 0;
	virtual FX_INT32			GetAllPagesSize(CFX_DWordArray& startIndexArray, CFX_FloatArray& widthArray, CFX_FloatArray& heightArray) = 0;
};

IFDRM_PDFSchema*	FDRM_PDFSchema_Create();

//*****************************************************************************
//* PDF encyption dictionary
//*****************************************************************************
class CFDRM_EncryptDictRead : public CFDRM_Object
{
public:
	virtual void	Release();

	FX_BOOL			Verify() const;

	FX_BOOL			GetFilter(CFX_WideString &wsFilter) const;
	FX_BOOL			GetSubFilter(CFX_WideString &wsSubFilter) const;
	FX_BOOL			GetVender(CFX_WideString &wsVender) const;
	FX_BOOL			GetIssuer(CFX_WideString &wsIssuer) const;
	FX_BOOL			GetCreator(CFX_WideString &wsCreator) const;
	FX_BOOL			GetFileId(CFX_WideString &wsFileId) const;
	FX_BOOL			GetFlowCode(CFX_WideString &wsFlowCode) const;
	FX_BOOL			GetOrder(CFX_WideString &wsOrder) const;	
	FX_BOOL			GetUser(CFX_WideString &wsUser) const;	
	FX_BOOL			GetServiceURL(CFX_WideString &wsServiceURL) const;

	FX_BOOL			GetKeyString(FX_BSTR key, CFX_WideString &wsStr) const;

protected:
	CFDRM_EncryptDictRead() : CFDRM_Object() {}
	virtual ~CFDRM_EncryptDictRead() {}
};

CFDRM_EncryptDictRead*	FDRM_EncryptDictRead_Load(CPDF_Dictionary *pEncryptDict);

class CFDRM_EncryptDictWrite : public CFDRM_EncryptDictRead
{
public:
	void			Save();

	void			SetFilter(FX_WSTR wsFilter);
	void			SetSubFilter(FX_WSTR wsSubFilter);
	void			SetVender(FX_WSTR wsVender);
	void			SetIssuer(FX_WSTR wsIssuer);
	void			SetCreator(FX_WSTR wsCreator);
	void			SetFileId(FX_WSTR wsFileId);
	void			SetFlowCode(FX_WSTR wsFlowCode);
	void			SetOrder(FX_WSTR wsOrder);
	void			SetUser(FX_WSTR wsUser);
	void			SetServiceURL(FX_WSTR wsServiceURL);

	void			SetKeyString(FX_BSTR key, FX_WSTR wsStr);

protected:
	CFDRM_EncryptDictWrite() : CFDRM_EncryptDictRead() {}
	virtual ~CFDRM_EncryptDictWrite() {}
};

CFDRM_EncryptDictWrite*	FDRM_EncryptDictWrite_Create(CPDF_Dictionary *pEncryptDict);

//*****************************************************************************
//* Foxit DRM Security Handler, version 2
//*
//* Do not register this security handler into PDF module manager, pass to CPDF_Parser directly.
//*****************************************************************************
class CFDRM_V2SecurityHandler : public CPDF_SecurityHandler
{
public:
	CFDRM_V2SecurityHandler();
	virtual ~CFDRM_V2SecurityHandler() {}

	virtual FX_BOOL				OnInit(CPDF_Parser* pParser, CPDF_Dictionary* pEncryptDict);
	virtual FX_DWORD			GetPermissions()				{return m_dwPermissions;}
	virtual FX_BOOL				IsOwner()						{return m_bOwner;}
	virtual FX_BOOL				GetCryptInfo(FX_INT32& cipher, FX_LPCBYTE& buffer, FX_INT32& keylen);
	virtual FX_BOOL				IsMetadataEncrypted()			{return m_bIsMetadataEncrypted;}
	virtual CPDF_CryptoHandler*	CreateCryptoHandler()			{return FX_NEW CPDF_StandardCryptoHandler;}

			void		SetParams(FX_BSTR subFilter, FX_BOOL bOwner, FX_DWORD dwPermissions, FX_INT32 nCipher, FX_INT32 nKeyLen, FX_BSTR fileID, FX_BSTR initialKey);

private:
	FX_BOOL				m_bOwner;
	FX_DWORD			m_dwPermissions;
	FX_BOOL				m_bIsMetadataEncrypted;
	FX_INT32			m_nCipher;
	FX_INT32			m_nKeyLen;
	FX_BYTE				m_EncryptKey[32];
};

#endif //_FDRM_PDF

/** @} */
